package org.nulldevice.tankshot.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;

import org.nulldevice.tankshot.Game;
import org.nulldevice.tankshot.GameContainer;
import org.nulldevice.tankshot.images.ImageContainer;
import org.nulldevice.tankshot.images.ImageRepresentant;

public class Tank extends Item implements java.awt.image.ImageObserver {

	public static final int MAXSHOOTINTERVAL = 200;

	protected static int speed = 3, turningSpeed = 5;

	protected int aimingX, aimingY;
	protected double aimingAngle = 0, neededDrivingAngle = 0, realPosX,
			realPosY;
	protected long lastShoot = 0;

	public double getDrivingAngle() {
		return angle;
	}

	public void setDrivingAngle(double drivingAngle) {
		this.angle = drivingAngle;
	}

	public double getNeededDrivingAngle() {
		return neededDrivingAngle;
	}

	public void setNeededDrivingAngle(double neededDrivingAngle) {
		this.neededDrivingAngle = neededDrivingAngle;
	}

	public void setAimingAngle(double aimingAngle) {
		this.aimingAngle = aimingAngle;
	}

	protected int bulletCounter = 0;
	protected int maxBulletCount = 5;
	protected int mineCounter = 0;
	protected int maxMineCount = 1;
	protected int direction = 0;

	static final int[] borderX = { -50, -58, -50, 32, 40, 32 };
	static final int[] borderY = { 23, 0, -23, -23, 0, 23 };

	// static final String baseImageName =
	// "/org/nulldevice/tankshot/images/tank1.png";
	// static final String cannonImageName =
	// "/org/nulldevice/tankshot/images/tank1cannon.png";
	// static final String explosionImageName =
	// "/org/nulldevice/tankshot/images/explosion.gif";

	static final int baseImageOffsetX = 68, // distance from image corner to
			// center of tank / cannon
			baseImageOffsetY = 60,
			cannonImageOffsetX = 81,
			cannonImageOffsetY = 74;

	protected ImageRepresentant cannonImage;
	// explosionImage;
	protected static ArrayList<BufferedImage> explosionAnimation = null;
	protected Iterator<BufferedImage> explosionAnimationIt;

	public Tank(int posX, int posY, int sizeX, int sizeY, String name,
			String ref, String ref2) {
		super(posX, posY, sizeX, sizeY, name, ref, baseImageOffsetX,
				baseImageOffsetY);
		cannonImage = ImageContainer.get().getImagerepresentant(ref2,
				cannonImageOffsetX, cannonImageOffsetY);

		hitpoints = 0;

		this.realPosX = posX;
		this.realPosY = posY;
		int[] borderPoints = { 0, 0, 0, 0, 0, 0 };
		border = new Polygon(borderPoints, borderPoints, borderX.length);
		recalcBorder = true;

		// try {
		// if (baseImage == null)
		// baseImage = ImageIO.read(getClass().getResource(baseImageName));
		// if (cannonImage == null)
		// cannonImage = ImageIO.read(getClass().getResource(
		// cannonImageName));
		// explosionImage =
		// ImageIO.read(getClass().getResource(explosionImageName));
		// if (explosionAnimation == null) {
		// explosionAnimation = new ArrayList<BufferedImage>();
		// Iterator<ImageReader> readers = ImageIO
		// .getImageReadersBySuffix("gif");
		// if (readers.hasNext()) {
		// ImageReader reader = readers.next();
		// reader.setInput(ImageIO.createImageInputStream(getClass()
		// .getResource(explosionImageName).openStream()));
		//
		// int n = reader.getNumImages(true);
		// for (int i = 0; i < n; i++) {
		// BufferedImage image = reader.read(i);
		// explosionAnimation.add(image);
		// }
		// explosionAnimationIt = explosionAnimation.iterator();
		//
		// } else
		// System.err.println("no image reader");
		// }
		// explosionAnimationIt = explosionAnimation.iterator();
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
	}

	public Polygon getBorder() {
		if (recalcBorder) {
			border.reset();

			for (int i = 0; i < borderX.length; i++) {
				border.xpoints[i] = getX()
						+ (int) ((((double) borderX[i]) * Math.cos(Math
								.toRadians(angle))) - (((double) borderY[i]) * Math
								.sin(Math.toRadians(angle))));
				border.ypoints[i] = getY()
						+ (int) ((((double) borderY[i]) * Math.cos(Math
								.toRadians(angle))) + (((double) borderX[i]) * Math
								.sin(Math.toRadians(angle))));
			}
			border.npoints = borderX.length;

			recalcBorder = false;
		}
		return this.border;
	}

	public void setAimingPoint(int x, int y) {
		this.aimingX = x;
		this.aimingY = y;
		updateAimingAngle();
	}

	public void updateAimingAngle() {
		this.aimingAngle = Math.toDegrees(Math.atan((double) (aimingY - posY)
				/ (double) (aimingX - posX)));
		if ((aimingX - posX) < 0)
			aimingAngle += 180;
		recalcBorder = true;
	}

	public double getAimingAngle() {
		return this.aimingAngle;
	}

	// private int getShortestTurn(int direction) {
	// int way1 = Math.abs(direction - (int) drivingAngle);
	// int way2 = Math.abs(((direction + 180) % 360) - (int) drivingAngle);
	// if (way1 > way2)
	// return (direction + 180) % 360;
	// else
	// return direction;
	// }

	protected double checkNewPosition(double newValue, double oldValue) {
		posX = (int) realPosX;
		posY = (int) realPosY;
		recalcBorder = true;

		if (GameContainer.getInstance().hitsSolidObject(this)) {
			recalcBorder = true;
			return oldValue;
		} else
			return newValue;
	}

	public boolean drive(int direction, double delta) {
		double oldPosX = realPosX;
		double oldPosY = realPosY;
		double oldDrivingAngle = angle;

		double factor = 0;
		if ((direction & Game.DIRECTION_UP) > 0)
			factor = 1;
		if ((direction & Game.DIRECTION_DOWN) > 0)
			factor = -1;

		if (factor != 0) {
			realPosX += Math.cos(Math.toRadians(angle)) * (double) speed
					* factor * delta;
			realPosX = checkNewPosition(realPosX, oldPosX);

			realPosY += Math.sin(Math.toRadians(angle)) * (double) speed
					* factor * delta;
			realPosY = checkNewPosition(realPosY, oldPosY);
		}
		posX = (int) realPosX;
		posY = (int) realPosY;
		recalcBorder = true;

		if ((direction & Game.DIRECTION_LEFT) > 0) {
			angle -= turningSpeed;
			if (GameContainer.getInstance().hitsSolidObject(this)) {
				angle = oldDrivingAngle;
				recalcBorder = true;
			}
		}

		if ((direction & Game.DIRECTION_RIGHT) > 0) {
			angle += turningSpeed;
			if (GameContainer.getInstance().hitsSolidObject(this)) {
				angle = oldDrivingAngle;
				recalcBorder = true;
			}
		}

		this.updateAimingAngle();

		if (posX == oldPosX && posY == oldPosY) {
			// nothing changed
			return false;
		}
		recalcBorder = true;
		return true;
	}

	// private void rotateAndDrive(int angle, int factorX, int factorY) {
	// double oldDrivingAngle = drivingAngle;
	// int oldPosX = posX;
	// int oldPosY = posY;
	//
	// neededDrivingAngle = getShortestTurn(angle);
	//
	// if (drivingAngle > neededDrivingAngle) {
	// drivingAngle -= turningSpeed;
	// rotateBorder();
	// if (GameContainer.getInstance().hitsSolidObject(this)) {
	// drivingAngle = oldDrivingAngle;
	// rotateBorder();
	// }
	// }
	// if (drivingAngle < neededDrivingAngle) {
	// drivingAngle += turningSpeed;
	// rotateBorder();
	// if (GameContainer.getInstance().hitsSolidObject(this)) {
	// drivingAngle = oldDrivingAngle;
	// rotateBorder();
	// }
	// }
	//
	// if (true || drivingAngle == neededDrivingAngle) {
	// posX = posX + (speed * factorX);
	// rotateBorder();
	// if (GameContainer.getInstance().hitsSolidObject(this)) {
	// posX = oldPosX;
	// rotateBorder();
	// }
	//
	// posY = posY + (speed * factorY);
	// rotateBorder();
	// if (GameContainer.getInstance().hitsSolidObject(this)) {
	// posY = oldPosY;
	// rotateBorder();
	// }
	// }
	//
	// }

	// protected void rotateBorder() {
	// int[] x2 = new int[borderX.length];
	// int[] y2 = new int[borderY.length];
	//
	// for (int i = 0; i < borderX.length; i++) {
	// x2[i] = posX
	// + (int) (((double) borderX[i] * Math.cos(Math
	// .toRadians(drivingAngle))) - ((double) borderY[i] * Math
	// .sin(Math.toRadians(drivingAngle))));
	// y2[i] = posY
	// + (int) (((double) borderY[i] * Math.cos(Math
	// .toRadians(drivingAngle))) + ((double) borderX[i] * Math
	// .sin(Math.toRadians(drivingAngle))));
	// }
	// border.xpoints = x2;
	// border.ypoints = y2;
	// border.npoints = x2.length;
	// }

	public void draw(Graphics g) {
		super.draw(g);

		// rotate tank
		// rotateAndDrawImage(g, baseImage, drivingAngle, baseImageOffsetX,
		// baseImageOffsetY);

		// if we are exploding, we don't need to draw the rest of the image
		if (isAboutToExplode())
			return;
		
		cannonImage.draw(g, getX(), getY(), aimingAngle);

		// rotate cannon
		// rotateAndDrawImage(g, cannonImage, aimingAngle, cannonImageOffsetX,
		// cannonImageOffsetY);

		// g.drawLine(posX, posY, aimingX, aimingY);
//		g.setColor(Color.red);
//
//		g.drawPolygon(this.border);

		// drawRect(posX - baseImageOffsetX, posY - baseImageOffsetY,
		// baseImageOffsetX, baseImageOffsetY);
		g.drawOval(posX, posY, 3, 3);
		g.setColor(Color.black);

		// if (aboutToExplode) {
		// if (explosionAnimationIt.hasNext()) {
		// BufferedImage i = explosionAnimationIt.next();
		// g.drawImage(i, posX - i.getWidth() / 2, posY - i.getHeight()
		// / 2, this);
		// } else
		// this.destroyed = true;
		// }
	}

	public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
		// System.out.println("Image update: flags=" + flags + " x=" + x + " y="
		// + y + " w=" + w + " h=" + h);
		// repaint();
		return true;
	}

	private void rotateAndDrawImage(Graphics g, BufferedImage image,
			double angle, int offsetX, int offsetY) {
		AffineTransform rotation;
		AffineTransformOp rotator;
		BufferedImage i;
		rotation = new AffineTransform();
		rotation.rotate(Math.toRadians(angle), offsetX, offsetY);
		rotator = new AffineTransformOp(rotation,
				AffineTransformOp.TYPE_BICUBIC);
		i = rotator.filter(image, null);
		g.drawImage(i, posX - offsetX, posY - offsetY, null);
	}

	public void setDirection(int dir) {
		this.direction = dir;
	}

	public int getDirection() {
		return this.direction;
	}

	public boolean move(double delta) {
		return this.drive(direction, delta);
	}

	// public boolean hitsWall(ArrayList<Wall> walls) {
	// if(walls != null) {
	// for(Wall wall : walls) {
	// Area a = new Area(border);
	//				
	// if(a.intersects(wall.getBorder())) {
	// //System.out.println("Hitting Wall");
	// return true;
	// }
	// //else System.out.println("Not Hitting Wall");
	// }
	// }
	// return false;
	// }

	public void shoot() {
		if (bulletCounter < maxBulletCount
				&& System.currentTimeMillis() - lastShoot > MAXSHOOTINTERVAL) {
			lastShoot = System.currentTimeMillis();
			// int tx = this.getX();
			// int ty = this.getY();

			double angle = this.getAimingAngle();

			// tx += Math.cos(Math.toRadians(angle)) * 70;
			// ty += Math.sin(Math.toRadians(angle)) * 70;
			Bullet b = new Bullet(this, getX(), getY(), angle, 4,
					Bullet.BULLETSIZE);

			GameContainer.getInstance().addItem(b);
			bulletCounter++;
		}
	}

	public void placeMine() {
		if (mineCounter < maxMineCount) {
			int tx = this.getX();
			int ty = this.getY();

			Mine m = new Mine(this, tx, ty);

			GameContainer.getInstance().addItem(m);
			mineCounter++;
		}
	}

	@Override
	public void hitItem(Item item) {
		if (item instanceof Bullet) {
			// Check if the bullet can hurt the tank
			if (!(((Bullet) item).isDeadlyForOwner() == false && ((Bullet) item)
					.getOwner() == this)) {
				hit();
				System.out.println("Hit by "
						+ ((Bullet) item).getOwner().getName() + "'s bullet !");
			}
		}
	}

	public void bulletDestroyed() {
		bulletCounter--;
	}

	public void mineDestroyed() {
		mineCounter--;
	}
}
